package com.lyn.plugin

import com.android.build.api.instrumentation.FramesComputationMode
import com.android.build.api.instrumentation.InstrumentationScope
import com.android.build.api.variant.AndroidComponentsExtension
import com.android.build.api.variant.Variant
import com.android.build.gradle.BaseExtension
import com.lyn.plugin.annotation.AnnotationScanClassVisitorFactory
import com.lyn.plugin.other.AnnotationScanResult
import org.gradle.api.Plugin
import org.gradle.api.Project
import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream

/**
 *
 * @author longyn 2025/07/04
 * @version 1.0.0
 */
class AndPlugin : Plugin<Project> {
    @Suppress("SdCardPath")
    override fun apply(target: Project) {
        target.plugins.withId("com.android.application") {
            val androidExtension = target.extensions.getByType(BaseExtension::class.java)
            androidExtension.aaptOptions.additionalParameters.addAll(
                listOf("--allow-reserved-package-id", "--package-id", "0x7a")
            )
            createTask(target)
            val androidComponents =
                target.extensions.getByType(AndroidComponentsExtension::class.java)
            androidComponents.onVariants { variant: Variant ->
                println("> Task :[AndPlugin]:variant:${variant.name}")
                val generatedOutput =
                    target.layout.buildDirectory.dir("generated/source/buildConfig/${variant.name}")
                generatedOutput.get().asFile.mkdirs()
                val intermediatesOutput =
                    target.layout.buildDirectory.dir("intermediates/javac/${variant.name}/compileDebugJavaWithJavac/classes")
                generatedOutput.get().asFile.mkdirs()
                variant.instrumentation.apply {
                    transformClassesWith(
                        AnnotationScanClassVisitorFactory::class.java, InstrumentationScope.ALL
                    ) {
                        it.generatedOutputDir.set(generatedOutput)
                        it.intermediatesOutputDir.set(intermediatesOutput)
                        it.buildType.set(variant.name)
                    }
                    setAsmFramesComputationMode(FramesComputationMode.COMPUTE_FRAMES_FOR_INSTRUMENTED_METHODS)
                }
            }
        }
    }

    @Suppress("SdCardPath")
    private fun createTask(target: Project) {
        target.tasks.register("pluginInstallDebug") {
            it.group = "andPlugin"
            it.description = "Build plugin Apk"
            it.dependsOn("assembleDebug")
            it.doLast {
                val buildDebugDir = target.layout.buildDirectory.dir("/outputs/apk/debug/")
                val buildApkFile =
                    File("${buildDebugDir.get().asFile.absolutePath}/${target.name}-debug.apk")
                if (!buildApkFile.exists()) {
                    throw RuntimeException("APK not found at ${buildApkFile.absolutePath}")
                }
                val pluginDebugDir = target.layout.projectDirectory.dir("/pluginDebug")
                pluginDebugDir.asFile.mkdirs()
                val file = File(pluginDebugDir.asFile, "${target.name}-debug-${AnnotationScanResult.PLUGIN_VERSION}.apk")
                if (!file.exists()) {
                    file.createNewFile()
                }
                val fileInputStream = FileInputStream(buildApkFile)
                val fileOutputStream = FileOutputStream(file)
                var len: Int
                val byteArray = ByteArray(1024)
                while (fileInputStream.read(byteArray).apply { len = this } != -1) {
                    fileOutputStream.write(byteArray, 0, len)
                }
                fileOutputStream.flush()
                fileOutputStream.close()
                fileInputStream.close()
            }
        }

        target.tasks.register("pluginInstallRelease") {
            it.group = "andPlugin"
            it.description = "Build plugin Apk and push to device"
            it.dependsOn("assembleRelease")
            it.doLast {
                val buildReleaseDir = target.layout.buildDirectory.dir("/outputs/apk/release/")
                val buildApkFile =
                    File("${buildReleaseDir.get().asFile.absolutePath}/${target.name}-release.apk")
                if (!buildApkFile.exists()) {
                    throw RuntimeException("APK not found at ${buildApkFile.absolutePath}")
                }
                val pluginReleaseDir = target.layout.projectDirectory.dir("/pluginRelease")
                pluginReleaseDir.asFile.mkdirs()
                val file = File(pluginReleaseDir.asFile, "${target.name}-debug-${AnnotationScanResult.PLUGIN_VERSION}.apk")
                if (!file.exists()) {
                    file.createNewFile()
                }
                val fileInputStream = FileInputStream(buildApkFile)
                val fileOutputStream = FileOutputStream(file)
                var len: Int
                val byteArray = ByteArray(1024)
                while (fileInputStream.read(byteArray).apply { len = this } != -1) {
                    fileOutputStream.write(byteArray, 0, len)
                }
                fileOutputStream.flush()
                fileOutputStream.close()
                fileInputStream.close()
            }
        }

    }
}